/*
 * Copyright (c) 2023 Huawei Technologies Co., Ltd.
 * openUBMC is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *         http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */

#include "hisport.h"

using namespace dev;

bool bus_hisport::init(mc::mutable_dict& csr_object) {
    try {
        if (csr_object.contains("Id")) {
            this->m_id = csr_object["Id"].as<uint8_t>();
        }
        if (csr_object.contains("BusNum")) {
            this->m_bus_num = csr_object["BusNum"].as<uint8_t>();
        }
        if (csr_object.contains("RegNum")) {
            this->m_reg_num = csr_object["RegNum"].as<uint8_t>();
        }
        if (csr_object.contains("FlagNum")) {
            this->m_flag_num = csr_object["FlagNum"].as<uint8_t>();
        }
        if (csr_object.contains("SendRegAddr")) {
            this->m_send_reg_addr = csr_object["SendRegAddr"].as<uint8_t>();
        }
        if (csr_object.contains("RecvRegAddr")) {
            this->m_recv_reg_addr = csr_object["RecvRegAddr"].as<uint8_t>();
        }
        if (csr_object.contains("FlagRegAddr")) {
            this->m_flag_reg_addr = csr_object["FlagRegAddr"].as<uint8_t>();
        }
        if (csr_object.contains("MaxBatchLen")) {
            this->m_max_batch_len = csr_object["MaxBatchLen"].as<uint8_t>();
        }
        if (csr_object.contains("MaxDataLen")) {
            this->m_max_data_len = csr_object["MaxDataLen"].as<uint32_t>();
        }
        if (csr_object.contains("MaxOffsetWidth")) {
            this->m_max_offset_width = csr_object["MaxOffsetWidth"].as<uint8_t>();
        }
        if (csr_object.contains("MaxLengthWidth")) {
            this->m_max_length_width = csr_object["MaxLengthWidth"].as<uint8_t>();
        }
        if (csr_object.contains("object_name")) {
            this->m_object_name = csr_object["object_name"].as<std::string>();
        }
        this->m_hisport_drv = std::make_unique<DRIVER_HISPORT::Hisport>();
        this->drv_init(this->m_hisport_drv.get());
        return true;
    } catch (const std::exception& e) {
        elog("Hisport init failed, exception: ${exception}", ("exception", e.what()));
        return false;
    }
}

void bus_hisport::drv_init(DRIVER_HISPORT::Hisport* m_hisport_drv) {
    DRIVER_HISPORT::HISPORT_INIT_S init_data;
    init_data.bus_num          = this->m_bus_num;
    init_data.reg_num          = this->m_reg_num;
    init_data.flag_num         = this->m_flag_num;
    init_data.send_reg_addr    = this->m_send_reg_addr;
    init_data.recv_reg_addr    = this->m_recv_reg_addr;
    init_data.flag_reg_addr    = this->m_flag_reg_addr;
    init_data.max_batch_len    = this->m_max_batch_len;
    init_data.max_data_len     = this->m_max_data_len;
    init_data.max_offset_width = this->m_max_offset_width;
    init_data.max_length_width = this->m_max_length_width;
    std::string bus_name       = "Hisport_" + std::to_string(this->m_id);
    m_hisport_drv->init(init_data, bus_name);
}

uint8_t bus_hisport::get_id() {
    return this->m_id;
}

void bus_hisport::bus_lock() {
    this->m_hisport_drv->lock();
}

void bus_hisport::bus_unlock() {
    this->m_hisport_drv->unlock();
}

uint32_t bus_hisport::read(ACCESS_DATA_S& data) {
    if (data.offset_width > 4) {
        MC_THROW(mc::invalid_arg_exception,
                 "Hisport read offset_width is too large, offset_width: ${offset_width}",
                 ("offset_width", data.offset_width));
    }

    if (data.offset == HISPORT_EXTEND_READ_MODE) {
        this->write(data);
    }

    // 准备读取参数
    DRIVER_HISPORT::HISPORT_DATA_S read_info{};
    read_info.bus_id       = this->get_id();
    read_info.addr         = data.addr;
    read_info.offset       = data.offset;
    read_info.offset_width = data.offset_width;
    read_info.data_width   = data.length;
    read_info.rx_len       = data.length;
    read_info.rw_flag      = 1;
    read_info.re_write_cnt = this->m_re_write_cnt;
    read_info.re_read_cnt  = this->m_re_read_cnt;

    // 执行读取操作
    try {
        auto read_result = this->m_hisport_drv->read(read_info);
        data.out_buffer.clear();
        data.out_buffer.assign(read_result.begin(), read_result.end());
        return 0;
    } catch (const std::exception& e) {
        MC_THROW(mc::invalid_arg_exception, "Hisport read failed, exception: ${exception}",
                 ("exception", e.what()));
    }
}

uint32_t bus_hisport::write(ACCESS_DATA_S& data) {
    if (data.offset_width > 4) {
        MC_THROW(mc::invalid_arg_exception,
                 "Hisport write offset_width is too large, offset_width: ${offset_width}",
                 ("offset_width", data.offset_width));
    }

    // 准备写入参数
    DRIVER_HISPORT::HISPORT_DATA_S write_info{};
    write_info.bus_id       = this->get_id();
    write_info.addr         = data.addr;
    write_info.offset       = data.offset;
    write_info.offset_width = data.offset_width;
    write_info.data_width   = data.length;
    write_info.rw_flag      = 0;
    write_info.re_write_cnt = this->m_re_write_cnt;
    write_info.re_read_cnt  = this->m_re_read_cnt;
    write_info.write_delay  = 0;

    // 执行写入操作
    try {
        std::string_view data_view(reinterpret_cast<const char*>(data.in_buffer.data()),
                                   data.in_buffer.size());
        this->m_hisport_drv->write(write_info, data_view);
        return 0;
    } catch (const std::exception& e) {
        MC_THROW(mc::invalid_arg_exception, "Hisport write failed, exception: ${exception}",
                 ("exception", e.what()));
    }
}